#include "MethodCall.h"
#include "ClassDefinition.h"
#include "../Exceptions.h"
#include "../GeneralVisitor.h"
#include "../Constants.h"

MethodCall::MethodCall(TreeNode* iObjectVariable, string iMethodName, vector<TreeNode*> iParameters)
{
    this->fObjectVariable = iObjectVariable;
    this->fMethodName = iMethodName;
    this->fParameters = iParameters;
    this->fDefinition = 0;
}

MethodCall::~MethodCall()
{
    for(vector<TreeNode*>::iterator it = fParameters.begin(); it != fParameters.end(); ++it)
    {
        delete (*it);
    }

    fParameters.clear();
    delete fObjectVariable;
}

void MethodCall::CheckVariables(set<string>& oVariables, string iParentName)
{
    this->fObjectVariable->CheckVariables(oVariables, iParentName);

    for(vector<TreeNode*>::iterator it = this->fParameters.begin(); it != this->fParameters.end(); ++it)
    {
        (*it)->CheckVariables(oVariables, iParentName);
    }
}

void MethodCall::CheckFunctionCalls(set<FunctionDefinition*>& iAllFunctions, string iParentName)
{
    for(vector<TreeNode*>::iterator it = this->fParameters.begin(); it != this->fParameters.end(); ++it)
    {
        (*it)->CheckFunctionCalls(iAllFunctions, iParentName);
    }

    if(this->fParameters.size() > MAX_METHOD_CALL_PARAMS)
        throw TooMuchCallParamCount(iParentName, fMethodName, MAX_METHOD_CALL_PARAMS, fParameters.size());
}

void MethodCall::CheckClassConstructions(set<ClassDefinition*>& iAllClasses, string iParentName)
{
    for(vector<TreeNode*>::iterator it = this->fParameters.begin(); it != this->fParameters.end(); ++it)
    {
        (*it)->CheckClassConstructions(iAllClasses, iParentName);
    }
}

void MethodCall::CheckExpressionTypes(map<string, ExpressionType*>& iVarTypes, string iParentName)
{
    this->fObjectVariable->CheckExpressionTypes(iVarTypes, iParentName);

    ExpressionType* lType = this->fObjectVariable->GetType(iVarTypes, iParentName);
    ValueType lValType = lType->fType;

    if(!lType->fResolveTypeAtRuntime)
    {
        if(lValType != UserDefinedObject)
        {
            delete lType;
            throw VariableIsNotAnObjectException(this->fObjectVariable->GetVarName(), iParentName);
        }

        if(!lType->fClassDef->MethodExists(this->fMethodName, this->fParameters.size()))
        {
            delete lType;
            throw MethodNotDeclaredException(fMethodName, fObjectVariable->GetVarName(), fParameters.size(), iParentName);
        }

        this->fDefinition = lType->fClassDef->GetMethodDefinition(this->fMethodName, this->fParameters.size());
    }

    delete lType;

    for(vector<TreeNode*>::iterator it = this->fParameters.begin(); it != this->fParameters.end(); ++it)
    {
        (*it)->CheckExpressionTypes(iVarTypes, iParentName);
    }
}

ExpressionType* MethodCall::GetType(map<string, ExpressionType*>& iVarTypes, string iParentName)
{
    this->CheckExpressionTypes(iVarTypes, iParentName);

    if(this->fDefinition != 0)
    {
        // znam definici - zjisti z ni navratovy typ a zkontroluj, ze to neco vraci
        ReturnStatement* lReturn = fDefinition->GetReturnStatement();

        if(lReturn == 0)
        {
            throw CallWithoutReturnException(iParentName, fMethodName, false);
        }

        return new ExpressionType(Unresolved);
    }
    else
        return new ExpressionType(Unresolved);
}

void MethodCall::Accept(GeneralVisitor* iVisitor)
{
    iVisitor->Visit(this);
}
